package com.liruo.apt;

import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Type.JCVoidType;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import javax.annotation.processing.Messager;
import javax.tools.Diagnostic;

/**
 * @Author:liruo
 * @Date:2023-10-18-23:56:41
 * @Desc
 */
public class DataTreeTranslator extends TreeTranslator {

  private final static String GETTER_PREFIX = "get";
  private final static String SETTER_PREFIX = "set";
  private final static String TOSTRING = "toString";
  private final Messager messager;
  private final TreeMaker treeMaker;
  private final Names names;

  public DataTreeTranslator(Messager messager, TreeMaker treeMaker, Names names) {
    this.messager = messager;
    this.treeMaker = treeMaker;
    this.names = names;
  }

  @Override
  public void visitClassDef(JCClassDecl jcClass) {
    //获取Variable即字段的定义
    boolean hasToString = false;
    Map<Name, JCVariableDecl> treeMap = new HashMap<>();
    HashSet<String> getAndSetterSet = new HashSet<>();
    for (JCTree def : jcClass.defs) {
      if (def.getKind().equals(Kind.VARIABLE)) {
        JCVariableDecl variableDecl = (JCVariableDecl) def;
        treeMap.put(variableDecl.getName(), variableDecl);
      } else if (def.getKind().equals(Kind.METHOD)) {
        MethodTree methodTree = (MethodTree) def;
        String methodName = methodTree.getName().toString();
        if (methodName.startsWith(GETTER_PREFIX) || methodName.startsWith(SETTER_PREFIX)) {
          getAndSetterSet.add(methodName);
        } else if (!hasToString && methodName.equals(TOSTRING)
            && methodTree.getParameters().size() == 0
            && methodTree.getReturnType().toString().equals(String.class.getName())) {
          hasToString = true;
        }
      }
    }

    treeMap.forEach((k, jcVariable) -> {
      messager.printMessage(Diagnostic.Kind.NOTE, String.format("fields:%s", k));
      try {
        //给类的定义jcClass.defs向前prepend添加方法(append向后添加), 一般是在构建器前(这里先生成getter,再setter, 生成的源码从上到下是先setter再getter)
        //增加get方法
        if(!getAndSetterSet.contains(Utils.lowCamel2Up(GETTER_PREFIX, k.toString()))){
          jcClass.defs = jcClass.defs.prepend(generateGetterMethod(jcVariable));
        }
        //增加set方法
        if(!getAndSetterSet.contains(Utils.lowCamel2Up(SETTER_PREFIX, k.toString()))){
          jcClass.defs = jcClass.defs.prepend(generateSetterMethod(jcVariable));
        }
      } catch (Exception e) {
        messager.printMessage(Diagnostic.Kind.ERROR, e.toString());
      }
    });
//    添加toString方法的定义
    if (!hasToString) {
      jcClass.defs = jcClass.defs.prepend(generateToStringBuilderMethod());
    }

    //设置修改结果
    super.visitClassDef(jcClass);
  }


  private JCMethodDecl generateGetterMethod(JCVariableDecl jcVariable) {
    //修饰符 public
    JCModifiers jcModifiers = treeMaker.Modifiers(Flags.PUBLIC);
    //方法名 get{VariableName}
    Name methodName = lowCamel2Up(jcVariable.getName(), GETTER_PREFIX);
    //返回值类型 {VariableType}
    JCExpression returnType = jcVariable.vartype;
    //泛型参数类型
    List<JCTypeParameter> typeParameters = List.nil();
    //参数
    List<JCVariableDecl> parameters = List.nil();
    //异常声明
    List<JCExpression> throwsClauses = List.nil();

    //方法体 return this.{VariableName}
    ListBuffer<JCStatement> jcStatements = new ListBuffer<>();
    jcStatements.append(
        treeMaker.Return(treeMaker.Select(treeMaker.Ident(nameOf("this")), jcVariable.getName())));
    JCBlock jcBlock = treeMaker.Block(0, jcStatements.toList());
    return treeMaker
        .MethodDef(jcModifiers, methodName, returnType, typeParameters, parameters, throwsClauses,
            jcBlock, null);
  }

  private JCMethodDecl generateSetterMethod(JCVariableDecl jcVariable) {
    //修饰符 public
    JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC);
    //方法名 set{VariableName}
    Name variableName = jcVariable.getName();
    Name methodName = lowCamel2Up(variableName, SETTER_PREFIX);
    //返回值类型 void
    JCExpression returnType = treeMaker.Type((new JCVoidType()));
    //泛型参数类型
    List<JCTypeParameter> typeParameters = List.nil();
    //参数 {VariableType} {VariableName}
    List<JCVariableDecl> parameters = List.of(treeMaker
        .VarDef(treeMaker.Modifiers(Flags.PARAMETER, List.nil()), jcVariable.name,
            jcVariable.vartype, null));
    //异常声明
    List<JCExpression> throwsClauses = List.nil();

    //方法体 this.{VariableName} = {VariableName}
    ListBuffer<JCStatement> jcStatements = new ListBuffer<>();
    jcStatements.append(treeMaker
        .Exec(treeMaker.Assign(treeMaker.Select(
                treeMaker.Ident(nameOf("this")), variableName),
            treeMaker.Ident(variableName))));
    JCBlock jcBlock = treeMaker.Block(0, jcStatements.toList());
    return treeMaker
        .MethodDef(modifiers, methodName, returnType, typeParameters, parameters, throwsClauses,
            jcBlock, null);

  }

  private JCMethodDecl generateToStringBuilderMethod() {
    //修饰符 public
    JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC);
    //返回值类型 String
    JCExpression returnType = memberAccess(String.class.getSimpleName());
    //方法名 toString
    Name methodName = nameOf(TOSTRING);
    //泛型参数 void
    List<JCTypeParameter> typeParameters = List.nil();
    //参数
    List<JCVariableDecl> parameters = List.nil();
    //异常声明
    List<JCExpression> throwsClauses = List.nil();

    //设置方法体 调用 我的Utils.toString()
    //调用一个方法, Apply(typeargs-泛型参数, meth-方法名, args, 参数)
    JCExpressionStatement statement = treeMaker.Exec(treeMaker.Apply(
        List.nil(),
        memberAccess(Utils.methodName(Utils.class, TOSTRING)),
        List.of(treeMaker.Ident(nameOf("this")))
    ));
    ListBuffer<JCStatement> jcStatements = new ListBuffer<>();
    jcStatements.append(treeMaker.Return(statement.getExpression()));
    JCBlock jcBlock = treeMaker.Block(0, jcStatements.toList());
    return treeMaker
        .MethodDef(modifiers, methodName, returnType, typeParameters, parameters, throwsClauses,
            jcBlock, null);
  }

  /**
   * @param components 表达式的字符串表示 (如java.lang.String, this.f, this.m())
   *                   Ident表示标识符, java.lang.String(3个标识符), this.f(2个表示符), this.m()(2个标识符)
   *                   Select: 2个JCExpression的连接, 如Select(expr(this), name(f))返回的JCExpression就是this.f
   * @return 表达式的对象表示
   */
  private JCExpression memberAccess(String components) {
    String[] componentArray = components.split("\\.");
    JCExpression expr = treeMaker.Ident(nameOf(componentArray[0]));
    for (int i = 1; i < componentArray.length; i++) {
      expr = treeMaker.Select(expr, nameOf(componentArray[i]));
    }
    return expr;
  }

  private Name lowCamel2Up(Name name, String prefix) {
    return names.fromString(Utils.lowCamel2Up(prefix, name.toString()));
  }


  private Name nameOf(String s) {
    return names.fromString(s);
  }
}